Skip to content

refactor(test/cli): migrate harness short-lived path to AppProcess + FileSystem#28366

Merged
kitlangton merged 2 commits into
devfrom
worktree-cli-effect-harness
May 19, 2026
Merged

refactor(test/cli): migrate harness short-lived path to AppProcess + FileSystem#28366
kitlangton merged 2 commits into
devfrom
worktree-cli-effect-harness

Conversation

@kitlangton

Copy link
Copy Markdown
Contributor

Summary

Slice 1 of 2 migrating the CLI subprocess test harness from raw Bun.spawn / cross-spawn wrappers to the Effect-native services already used elsewhere in the codebase.

This slice covers the two lower-risk surfaces:

  1. Tmpdiros.tmpdir + fs.mkdir + addFinalizerFileSystem.makeTempDirectoryScoped({ prefix: "oc-cli-" }). Cleanup is now scope-driven without a hand-rolled finalizer.

  2. Short-lived spawnProcess.run (legacy cross-spawn wrapper from src/util/process.ts) → AppProcess.run, which is the same service already used by git, ripgrep, format, mcp, etc. Single spawn primitive across src + tests.

The long-lived serve / acp builders still use raw Bun.spawn — those land in slice 2.

Notes

  • stdin: "ignore" set explicitly. ChildProcess.make defaults to "pipe", which made opencode run block on Bun.stdin.text() waiting for EOF that never came. Old Process.run defaulted to ignore — this preserves the prior contract.
  • A timeout surfaces as AppProcessError; converted via timeoutOrElse to a synthetic non-zero result so expectExit still drives the failure path.
  • AppFileSystem.defaultLayer + AppProcess.defaultLayer provided inside withCliFixture so external call sites + CliFixture shape are unchanged.
  • Help-snapshot path regex widens to [A-Za-z0-9]+ — Node's mkdtemp suffix is mixed-case, unlike the prior Math.random().toString(36).

Test plan

  • bun run typecheck clean
  • bun run test test/cli/ — 333 pass, 0 fail (was 333 pass with this diff applied; the 4 prior timeouts were the stdin issue caught + fixed before merge)
  • CI green

…rvices

Replace the tmpdir + short-lived spawn machinery in withCliFixture:

- `os.tmpdir + fs.mkdir + addFinalizer` → `FileSystem.makeTempDirectoryScoped`.
  Cleanup is now scope-driven without a hand-rolled finalizer.

- `Process.run` (legacy cross-spawn wrapper) → `AppProcess.run`, which is the
  same service already used by `git`, `ripgrep`, `format`, `mcp`, etc.
  One spawn primitive across src + tests.

`stdin: "ignore"` is set explicitly because `ChildProcess.make` defaults
stdin to "pipe", which caused `opencode run` to block on `Bun.stdin.text()`
waiting for an EOF that the test never sent. The old wrapper defaulted to
ignore, so this preserves the prior contract.

`AppFileSystem.defaultLayer` + `AppProcess.defaultLayer` are provided
internally so callers' signatures don't change.

The help-snapshot path regex widens from `[a-z0-9]+` to `[A-Za-z0-9]+`
because Node's `mkdtemp` suffix is mixed-case (unlike the prior
`Math.random().toString(36)` suffix).

Slice 1 of 2. Slice 2 will migrate the long-lived `serve` / `acp`
builders, which use raw `Bun.spawn` today.
Two simplify-pass refinements on top of the harness migration:

1. Pass `timeout` into `AppProcess.run` instead of wrapping with an
   external `Effect.timeoutOrElse`. AppProcess.run is itself scoped, so
   the built-in timeout triggers the acquireRelease kill finalizer
   *before* surfacing the error — guaranteeing the child is dead by the
   time the test continues. The external wrapper variant raced the
   scope close and could leak the child past the test boundary.

2. Replace `Effect.orDie` with `Effect.catchTag("AppProcessError", ...)`
   so timeouts AND spawn failures both turn into a synthetic non-zero
   result with the error's command / stderr / cause as diagnostics —
   instead of crashing as a defect. The synthetic result is `satisfies
   AppProcess.RunResult` so it stays in sync with process.ts.

3. Name the spawn Effect span `"opencode.spawn"` via `Effect.fn` to
   match the existing `Effect.fn("opencode.serve")` / `.acp` siblings.
@kitlangton kitlangton merged commit 80e5fb1 into dev May 19, 2026
12 of 14 checks passed
@kitlangton kitlangton deleted the worktree-cli-effect-harness branch May 19, 2026 20:18
AIALRA-0 pushed a commit to AIALRA-0/opencode-turn-engine that referenced this pull request Jun 10, 2026
AIALRA-0 pushed a commit to AIALRA-0/opencode-turn-engine that referenced this pull request Jun 10, 2026
avion23 pushed a commit to avion23/opencode that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant